﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SofireCodeGenerator.Core;
using Sofire.Data;
using ICSharpCode.TextEditor.Document;
using System.Text.RegularExpressions;
using AutocompleteMenuNS;
using System.IO;

namespace SofireCodeGenerator.UI
{
    /*
     * 1、增加配置文件（实体名称后缀、是否自动识别表名和列表等配置信息）
     * 2、增加路径输出。
     * 3、考虑自动创建文件夹（VSE）
     */
    public partial class MainForm : Form
    {

        public readonly static Icon FormIcon = Icon.FromHandle(Properties.Resources.MainForm.GetHicon());
        private readonly ContextMenu TreeViewContextMenu;
        public MainForm()
        {
            InitializeComponent();
            GlobalParameters.OutputDirectory = GA.AppDirectory;
            this.StartPosition = FormStartPosition.CenterScreen;
            this.Icon = FormIcon;
            tview_Objects.ImageList = new ImageList();
            tview_Objects.ImageList.Images.Add(Properties.Resources.Table);
            tview_Objects.ImageList.Images.Add(Properties.Resources.View);
            tview_Objects.AfterCheck += new TreeViewEventHandler(tview_Objects_AfterCheck);
            tview_Objects.CheckBoxes = true;
            tview_Objects.MouseDown += new MouseEventHandler(tview_Objects_MouseDown);

            TreeViewContextMenu = new System.Windows.Forms.ContextMenu();
            TreeViewContextMenu.MenuItems.Add("实体生成器(&E)", Item_EntityGenerator).Shortcut = Shortcut.F1;
            TreeViewContextMenu.MenuItems.Add("SQL 生成器(&G)", Item_SqlGenerator).Shortcut = Shortcut.F3;
            //run.Visible = false;

            tview_Objects.ContextMenu = TreeViewContextMenu;

            textEditorControl1.TextEditorProperties.Font = new Font("YaHei Consolas Hybrid", 10F);
            textEditorControl1.Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy("TSQL");

            textEditorControl1.ActiveTextAreaControl.TextArea.KeyDown += new KeyEventHandler(TextArea_KeyDown);
            textEditorControl1.ActiveTextAreaControl.VScrollBar.ValueChanged += new EventHandler(VScrollBar_ValueChanged);
            textEditorControl1.ActiveTextAreaControl.HScrollBar.ValueChanged += new EventHandler(VScrollBar_ValueChanged);
            //   private AutocompleteMenuNS.AutocompleteMenu autocompleteMenu1;
            //this.autocompleteMenu1 = new AutocompleteMenuNS.AutocompleteMenu();
            //this.autocompleteMenu1.SetAutocompleteMenu(textEditorControl1, this.autocompleteMenu1);
            //this.autocompleteMenu1.AllowsTabKey = true;
            //this.autocompleteMenu1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F);
            //this.autocompleteMenu1.SearchPattern = "[\\w\\.:=!<>]";

            //var items = new List<AutocompleteItem>();
            //var hs = textEditorControl1.Document.HighlightingStrategy as DefaultHighlightingStrategy;
            //foreach(var item in File.ReadAllLines("KeyWords.txt", Encoding.UTF8))
            //{
            //    if(string.IsNullOrEmpty(item)) continue;
            //    items.Add(new AutocompleteItem(item));
            //}

            //items.Add(new InsertSpaceSnippet());
            //items.Add(new InsertSpaceSnippet(@"^(\w+)([=<>!:]+)(\w+)$"));
            //items.Add(new InsertEnterSnippet());
            //autocompleteMenu1.SetAutocompleteItems(items);
        }

        ///// <summary>
        ///// Divides numbers and words: "123AND456" -> "123 AND 456"
        ///// Or "i=2" -> "i = 2"
        ///// </summary>
        //class InsertSpaceSnippet : AutocompleteItem
        //{
        //    string pattern;

        //    public InsertSpaceSnippet(string pattern)
        //        : base("")
        //    {
        //        this.pattern = pattern;
        //    }

        //    public InsertSpaceSnippet()
        //        : this(@"^(\d+)([a-zA-Z_]+)(\d*)$")
        //    {
        //    }

        //    public override CompareResult Compare(string fragmentText)
        //    {
        //        if(Regex.IsMatch(fragmentText, pattern))
        //        {
        //            Text = InsertSpaces(fragmentText);
        //            if(Text != fragmentText)
        //                return CompareResult.Visible;
        //        }
        //        return CompareResult.Hidden;
        //    }

        //    public string InsertSpaces(string fragment)
        //    {
        //        var m = Regex.Match(fragment, pattern);
        //        if(m.Groups[1].Value == "" && m.Groups[3].Value == "")
        //            return fragment;
        //        return (m.Groups[1].Value + " " + m.Groups[2].Value + " " + m.Groups[3].Value).Trim();
        //    }

        //    public override string ToolTipTitle
        //    {
        //        get
        //        {
        //            return Text;
        //        }
        //    }
        //}

        ///// <summary>
        ///// Inerts line break after '}'
        ///// </summary>
        //class InsertEnterSnippet : AutocompleteItem
        //{
        //    int enterPlace = 0;

        //    public InsertEnterSnippet()
        //        : base("[Line break]")
        //    {
        //    }

        //    public override CompareResult Compare(string fragmentText)
        //    {
        //        var tb = Parent.TargetControlWrapper;

        //        var text = tb.Text;
        //        for(int i = Parent.Fragment.Start - 1 ; i >= 0 ; i--)
        //        {
        //            if(text[i] == '\n')
        //                break;
        //            if(text[i] == '}')
        //            {
        //                enterPlace = i;
        //                return CompareResult.Visible;
        //            }
        //        }

        //        return CompareResult.Hidden;
        //    }

        //    public override string GetTextForReplace()
        //    {
        //        var tb = Parent.TargetControlWrapper;

        //        //insert line break
        //        tb.SelectionStart = enterPlace + 1;
        //        tb.SelectedText = "\n";
        //        Parent.Fragment.Start += 1;
        //        Parent.Fragment.End += 1;
        //        return Parent.Fragment.Text;
        //    }

        //    public override string ToolTipTitle
        //    {
        //        get
        //        {
        //            return "Insert line break after '}'";
        //        }
        //    }
        //}

        void VScrollBar_ValueChanged(object sender, EventArgs e)
        {
            this.Text = (sender as ScrollBar).Value.ToString();
        }

        void TextArea_KeyDown(object sender, KeyEventArgs e)
        {
            if(e.KeyCode == Keys.F5)
            {
                tsb_Run_Click(tsb_Run, EventArgs.Empty);
                e.Handled = true;
                e.SuppressKeyPress = false;
            }
        }

        void tview_Objects_MouseDown(object sender, MouseEventArgs e)
        {
            if(e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                var node = tview_Objects.GetNodeAt(e.Location);
                tview_Objects.ContextMenu = null;
                if(node == null) return;
                if(node.Tag is OracleObjectType) tview_Objects.ContextMenu = TreeViewContextMenu;

                tview_Objects.SelectedNode = node;

                //   TreeViewContextMenu.Show(tview_Objects, e.Location);
            }
        }

        void Item_SqlGenerator(object sender, EventArgs e)
        {
            if(tview_Objects.SelectedNode.Tag is OracleObjectType)
            {
                SqlGeneratorForm form = new SqlGeneratorForm(this._Generator, tview_Objects.SelectedNode.Text);
                if(form.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    WriteToEditor(form.ResultSql);
                }
            }
        }

        void Item_EntityGenerator(object sender, EventArgs e)
        {
            if(tview_Objects.SelectedNode.Tag is OracleObjectType)
            {
                EntityGeneratorForm form = new EntityGeneratorForm(this._Generator, tview_Objects.SelectedNode.Text);
                if(form.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    //WriteToEditor(form.ResultSql);
                }
            }
        }
        void tview_Objects_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if(e.Node.Tag is OracleObjectType) return;
            this.CheckNodes(e.Node.Nodes, e.Node.Checked);
        }

        private void CheckNodes(TreeNodeCollection nodes, bool isChecked)
        {
            foreach(TreeNode node in nodes)
            {
                node.Checked = isChecked;
                if(node.Nodes.Count > 0) this.CheckNodes(node.Nodes, isChecked);
            }
        }

        QueryEngineBase _Engine;
        SqlGeneratorBase _Generator;
        private void EnabledControls(bool login)
        {
            for(int i = 1 ; i < toolStrip1.Items.Count ; i++)
            {
                toolStrip1.Items[i].Enabled = login;
            }
            textEditorControl1.Enabled = login;
            tview_Objects.Enabled = login;
            if(!login)
            {
                tview_Objects.Nodes.Clear();
            }
        }

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            var connectinString = Properties.Settings.Default.ConnectionString;
            var provider = (QueryEngineProvider)Properties.Settings.Default.DatabaseProvider;
            if(!string.IsNullOrEmpty(connectinString))
            {
                this._Engine = VSEx.CreateEngine(provider, connectinString);
            }
            else
            {
                this.ShowLogin();
            }
            this.InitEngine();
        }

        private static readonly object DirectoryNodeTag = new object();

        private TreeNode FindDirectoryNode(TreeNodeCollection coll, string direName)
        {
            foreach(TreeNode node in coll)
            {
                if(node.Tag == DirectoryNodeTag && node.Text == direName) return node;
            }
            return null;
        }

        private TreeNode AppendNodeTo(TreeNode parent, string text, int imageIndex, object tag)
        {
            TreeNode node = tag == DirectoryNodeTag ? this.FindDirectoryNode(parent.Nodes, text) : null;
            if(node == null)
            {
                node = new TreeNode(text);
                parent.Nodes.Add(node);
                node.Tag = tag;
                node.ImageIndex = imageIndex;
                node.SelectedImageIndex = imageIndex;
            }
            return node;
        }

        private void InitObjects(DataTable table, OracleObjectType type)
        {
            tview_Objects.BeginUpdate();
            var parent = new TreeNode(type.ToString());
            tview_Objects.Nodes.Add(parent);
            var imageIndex = (int)type;
            parent.ImageIndex = imageIndex;
            parent.SelectedImageIndex = imageIndex;
            foreach(DataRow row in table.Rows)
            {
                var objectName = Convert.ToString(row[0]);
                var splicIndex = objectName.IndexOf('_');
                var childParent = parent;
                if(splicIndex > 0)
                {
                    var direName = objectName.Substring(0, splicIndex);
                    if(!(string.Equals(direName, "v", StringComparison.CurrentCultureIgnoreCase)
                        || string.Equals(direName, "t", StringComparison.CurrentCultureIgnoreCase)))
                        childParent = this.AppendNodeTo(childParent, direName, imageIndex, DirectoryNodeTag);
                }
                this.AppendNodeTo(childParent, objectName, imageIndex, type);

            }
            tview_Objects.EndUpdate();
        }

        private void InitEngine()
        {
            this.EnabledControls(false);
            VSEx.ColumnsCache.Clear();
            if(this._Engine != null && this.IsSuccess(this._Engine.TestConnection()))
            {
                switch(this._Engine.Provider)
                {
                    case QueryEngineProvider.Oracle:
                        this._Generator = new OracleGenerator(this._Engine);
                        break;
                }
                var r = this._Generator.GetUserTables();
                if(!this.IsSuccess(r)) return;
                this.InitObjects(r.Value, OracleObjectType.TABLE);

                r = this._Generator.GetUserViews();
                if(!this.IsSuccess(r)) return;
                this.InitObjects(r.Value, OracleObjectType.VIEW);

                this.EnabledControls(true);
            }
        }

        private void WriteToEditor(string context)
        {
            textEditorControl1.Text += "\r\n" + context;
            textEditorControl1.Refresh();
        }

        private void ShowLogin()
        {
            var loginForm = new LoginForm();
            if(loginForm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                this._Engine = loginForm.Engine;
            }
        }

        private void tsb_ConnectionManager_Click(object sender, EventArgs e)
        {
            this.ShowLogin();
            this.InitEngine();
        }

        private void tsb_Run_Click(object sender, EventArgs e)
        {
            var sqlCommandText = textEditorControl1.ActiveTextAreaControl.TextArea.SelectionManager.SelectedText;
            if(string.IsNullOrEmpty(sqlCommandText))
                sqlCommandText = textEditorControl1.Text;
            if(string.IsNullOrEmpty(sqlCommandText))
            {
                this.ShowError("请输入查询语句！");
                return;
            }
            var r = this._Engine.ExecuteTable(sqlCommandText);
            if(this.IsSuccess(r))
            {
                if(r.Value.Rows.Count == 1 && r.Value.Columns.Count == 1)
                {
                    tps_Tip.Text = Convert.ToString(r.Value.Rows[0][0]);
                    return;
                }
                new ResultDataForm(r).Show();
            }
        }

        public void GetSelectedObjects(List<string> tableNames, TreeNodeCollection nodes)
        {
            foreach(TreeNode node in nodes)
            {
                if(node.Checked && node.Tag is OracleObjectType)
                {
                    tableNames.Add(node.Text);
                    continue;
                }
                this.GetSelectedObjects(tableNames, node.Nodes);
            }
        }
        private void tsb_Build_Click(object sender, EventArgs e)
        {
            List<string> tableNames = new List<string>();
            GetSelectedObjects(tableNames, tview_Objects.Nodes);
            if(tableNames.Count == 0) return;
            foreach(var tableName in tableNames)
            {
                var r = VSEx.RefreshColumns(this._Generator, tableName);
                if(!this.IsSuccess(r))
                {
                    this.ShowError(r);
                    return;
                }
                EntityGenerator gen = new EntityGenerator()
                {
                    AllColumns = false,
                    ClassName = VSEx.GetAliasName(tableName) + "Entity",
                    ColumnsTable = r.Value,
                    Generator = this._Generator,
                    TableName = tableName
                };
                string entityCode, xsqlCode;
                gen.Create(out entityCode, out xsqlCode);
                new PreviewForm(tableName,entityCode, xsqlCode).ShowDialog();
            }
        }

        private void tsb_Setting_Click(object sender, EventArgs e)
        {
            new SettingForm().ShowDialog();
        }

    }
}
